/**
 * gwidgets.js
 *
 * Widgets based on Prototype JS library
 *
 * @copyright Gerald Estadieu (c) 2007 All Rights Reserved
 * @authors Gerald Estadieu <gestadieu@gmail.com>
 * @url http://gestadieu.free.fr/gwidgets/
 * @version 0.5
 * @license License: http://creativecommons.org/licenses/GPL/
 */
var gWidget = Base.extend({
   Version: '0.5',
   boxWaiting: new Template('<div class="gwidget-waiting"><img src="#{loadingImg}" alt="loading..." /> #{msgWaiting}</div>'),
   boxError: new Template('<div class="gwidget-error">#{msgError}</div>'),
   errorInline: 'The target element id does not seem to exist in this page...',
   linkRefresh: new Template('<a href="#{href}" id="#{id}" class="gwidget-refresh"><img src="#{refreshImg}" alt="Refresh" title="Refresh"/></a>'),
   classXHRLoaded: 'XHRLoaded',

   constructor: function() {
      this.options = Object.extend({
         msgError: 'Sorry, it seems there is a communication problem with our server, try later...',
         msgWaiting: 'Please wait while loading...',
         msgClose: 'close',
         pathImg: 'images/',
         arrowLeft: 'gexpander-arrow-left.png',
         arrowBottom: 'gexpander-arrow-bottom.png',
         loadingImg: 'loading.gif',
         refreshImg: 'refresh-icon.png',
         toggleEffect: 'appear',
         useRefresh: true,
         scroll: true,
         defaultWidth: '450',
         defaultHeight: '350',
         xMin: 100,
         yMin: 100,
         contentPadding: 20,
         eventOn: 'mouseover',
         eventOff: 'mouseout'
      },(typeof(gWidget_Options)=='object')?gWidget_Options:{});
      if (!(typeof(Effect)=='object')) this.options.toggleEffect = '';
   },

   _XHRUpdater: function(url,content) {
      var xhr = new Ajax.Updater(
         { success: content},
         url,
         {
            onFailure: function() {
               $(content).update(this.boxError.evaluate({msgError:this.options.msgError}));
            }.bind(this),
            onLoading: function() {
               $(content).update(this.boxWaiting.evaluate({loadingImg:this.options.pathImg+this.options.loadingImg,msgWaiting:this.options.msgWaiting}));
            }.bind(this),
            onComplete: function() {
               if (this.options.useRefresh) {
                  new Insertion.Top(content,this.linkRefresh.evaluate({href:url,refreshImg:this.options.pathImg+this.options.refreshImg,id:content+'-refresh'}));
                  Event.observe(content + '-refresh','click',this._XHRRefresh.bind(this),false);
               }
               if (typeof(this.loaded) == 'function') { this.loaded(content); }
            }.bind(this)
         }
      );
   },

   _XHRRefresh: function(evt) {
      var elt = Event.element(evt);
      $(elt).up('div').removeClassName(this.classXHRLoaded);
      if (typeof(this.refresh) == 'function') { this.refresh(elt); }
      Event.stop(evt);
   },

   _getUrl: function(elt) {
      if (elt.readAttribute(this.widgetName)) {
         var tpelt = document.createElement('a');
         tpelt.setAttribute('href',elt.readAttribute(this.widgetName));
         elt = tpelt;
      }
      var params = (elt.search)?elt.search.substr(1).toQueryParams():'';
      var container = (eval('params.' + this.widgetName))?eval('params.' + this.widgetName):elt.hash.substr(1);
      var url = {
         url: elt.href,
         baseUrl: elt.pathname,
         container: container,
         params: params,
         isXHR: (elt.href.split('#').first().split('?').first()!=window.location.href.split('#').first().split('?').first())?true:false
      };
      if (this.widgetName == 'gtip' && $(container)) { url.isXHR = false; }
      return url;
   },

   viewportDimensions: function(){
      var de = document.documentElement;
      var w = window.innerWidth || self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
      var h = window.innerHeight || self.innerHeight || (de&&de.clientHeight) || document.body.clientHeight;
      return {xWidth: w, yHeight: h};
   },

   pageDimensions: function(){
      var xScroll;
      var yScroll;
      if (window.innerHeight && window.scrollMaxY || window.innerWidth && window.scrollMaxX) {
         yScroll = window.innerHeight + window.scrollMaxY;
        xScroll = window.innerWidth + window.scrollMaxX;
        var deff = document.documentElement;
        var wff = (deff&&deff.clientWidth) || document.body.clientWidth || window.innerWidth || self.innerWidth;
        var hff = (deff&&deff.clientHeight) || document.body.clientHeight || window.innerHeight || self.innerHeight;
        xScroll -= (window.innerWidth - wff);
        yScroll -= (window.innerHeight - hff);
      } else if (document.body.scrollHeight > document.body.offsetHeight || document.body.scrollWidth > document.body.offsetWidth){ // all but Explorer Mac
         yScroll = document.body.scrollHeight;
        xScroll = document.body.scrollWidth;
      } else {
         yScroll = document.body.offsetHeight;
        xScroll = document.body.offsetWidth;

      }
      return {xWidth: xScroll, yHeight: yScroll};
   },

   scrollPosition: function(){
      var yScrolltop;
      var xScrollleft;
      if (self.pageYOffset || self.pageXOffset) {
         yScrolltop = self.pageYOffset;
         xScrollleft = self.pageXOffset;
      } else if (document.documentElement && document.documentElement.scrollTop || document.documentElement.scrollLeft ){    // Explorer 6 Strict
         yScrolltop = document.documentElement.scrollTop;
         xScrollleft = document.documentElement.scrollLeft;
      } else if (document.body) {// all other Explorers
         yScrolltop = document.body.scrollTop;
         xScrollleft = document.body.scrollLeft;
      }
      return {xOffset: xScrollleft, yOffset: yScrolltop};
   }
});

/**
 * gTab
 *
 * @author Gerald Estadieu
 * @version 1.0 2007-02-20
 */
var gTab = gWidget.extend({
   widgetName: 'gtab',
   classHide: 'gtab-hide',
   classActive: 'gtab-active',
   classLoading: 'gtab-loading',

   constructor: function(elt) {
      this.elt = elt;
      this.eltController = this.elt + '-controller';
      this.base();
      this.menu = $(this.elt).down('ul').getElementsBySelector('li a');
      this.setup();
      this.menu.each(this.observer.bind(this));
      this.show(this.getInitialTab());
   },


   setup: function() {
      $(this.elt).down('ul').addClassName(this.widgetName + '-controllers');
      this.menu.each(function(elt){
         var url = this._getUrl(elt);
         $(elt).setAttribute('id',url.container + '-controller');
      }.bind(this));
   },

   observer: function(elt) {
      Event.observe(elt,'click',this.activate.bindAsEventListener(this),false);
      this.hide(elt);
   },

   activate: function(evt) {
      var elt = Event.findElement(evt, "a");
      elt.blur();
      this.show(elt);
      this.menu.without(elt).each(this.hide.bind(this));
      Event.stop(evt);
   },

   hide: function(elt) {
      var url = this._getUrl(elt);
      $(elt,url.container).invoke('addClassName',this.classHide);
      $(elt,url.container).invoke('removeClassName',this.classActive);
   },

   refresh: function(elt) {
      var elt = elt.up('div');
      var eltController = $(elt).readAttribute('id')+'-controller';
      this.show($($(elt).readAttribute('id')+'-controller'));
   },

   loaded: function(elt) {
      $($(elt).readAttribute('id')+'-controller').removeClassName(this.classLoading);
   },

   show: function(elt) {
      var url = this._getUrl(elt);
      $(elt,url.container).invoke('removeClassName',this.classHide);
      $(elt,url.container).invoke('addClassName',this.classActive );

      if (url.isXHR && !$(url.container).hasClassName(this.classXHRLoaded)) {
         $(elt).addClassName(this.classLoading);
         this._XHRUpdater(url.url,url.container);
         $(url.container).addClassName(this.classXHRLoaded);
      }
    },

   getInitialTab: function() {
      if(document.location.href.match(/#(\w.+)/)) {
         var loc = RegExp.$1;
         var elt = this.menu.find(function(value) { return value.href.match(/#(\w.+)/)[1] == loc; });
         return elt || this.menu.first();
      } else {
         return this.menu.first();
      }
   }
});

/*
 * gExpander
 * Gerald Estadieu
 * version 1.0 2007-02-20
 */
var gExpander = gWidget.extend({
   widgetName: 'gexpander',
   imgController: new Template('<img src="#{pathImg}" class="gexpander-img" />'),
   containerHtml: new Template('<div id="#{id}" style="display:none;"></div>'),
   classController: 'gexpander-controller',

   constructor: function(elt) {
      this.base();
      if (elt) { this.observer($(elt)); }
      else { document.getElementsByClassName(this.widgetName).each(this.observer.bind(this));}
   },

   observer: function(elt) {
      var url = this._getUrl(elt);
      new Insertion.Top(elt,this.imgController.evaluate({pathImg: this.options.pathImg + this.options.arrowLeft}));
      Event.observe(elt,'click',this.activate.bindAsEventListener(this),false);
      if (url.container && !$(url.container)) {
         new Insertion.After(elt,this.containerHtml.evaluate({id:url.container}));
      }
   },

   activate: function(evt) {
      Event.stop(evt);
      var elt = Event.element(evt);
      elt.blur();
      this.show(elt);
      var img = $(elt).down('img.gexpander-img');
      var url = this._getUrl(elt);
      if (!$(url.container).visible() && img) {
         img.src = this.options.pathImg + this.options.arrowBottom;
      }   else {
         img.src = this.options.pathImg + this.options.arrowLeft;
      }
      if (this.options.toggleEffect) {
         Effect.toggle($(url.container),this.options.toggleEffect,{duration:0.3});
      } else {
         var tp = (!$(url.container).visible())?$(url.container).show():$(url.container).hide();
      }
   },

   refresh: function(elt){
      this.show(elt.up('a.gwidget-refresh'));
   },

   show: function(elt) {
      var url = this._getUrl(elt);
      if (url.isXHR && !$(url.container).hasClassName(this.classXHRLoaded)) {
         this._XHRUpdater(url.url,url.container);
         $(url.container).addClassName(this.classXHRLoaded);
      }
   }
});

/**
 * gBox
 * Create a simple modal window with inline or ajax content
 *
 */
var gBox = gWidget.extend({
   widgetName: 'gbox',
   boxHtml: '<div id="gbox_loading" style="display:none"></div><iframe id="gbox_frame" style="display:none"></iframe><div id="gbox_overlay" style="display:none">&nbsp;</div><div id="gbox_window" style="display:none"><div id="gbox_window_title"></div><div id="gbox_window_content" class="gBox"></div></div>',
   imgHtml: new Template('<img src="#{url}" width="#{width}" height="#{height}" id="gbox_img" alt="#{alt}"/>'),
   closeHtml: new Template('<span id="gbox_close">#{close}</span>'),
   idOverlay: 'gbox_overlay',
   idWindow: 'gbox_window',
   idLoading: 'gbox_loading',
   idFrame: 'gbox_frame',
   idTitle: 'gbox_window_title',
   idContent: 'gbox_window_content',
   idClose: 'gbox_close',
   idImg: 'gbox_img',

   constructor: function(elt){
      this.base();
      if (!$(this.idOverlay)) this.insertContainer();
      if (elt) { this.observer($(elt)); }
      else { document.getElementsByClassName(this.widgetName).each(this.observer.bind(this));   }
   },

   observer: function(elt){
      Event.observe(elt,'click',this.activate.bindAsEventListener(this),false);
      //if (this.options.scroll) { Event.observe(window,'scroll',this.resize.bindAsEventListener(this),false); }
      //if (this.options.resize) { Event.observe(window,'resize',this.resize.bindAsEventListener(this),false); }
   },

   insertContainer: function(){
      new Insertion.Bottom(document.getElementsByTagName('body')[0],this.boxHtml);
      this.hide();
      Event.observe(this.idOverlay,'click',this.hide.bindAsEventListener(this),false);
   },

   activate: function(evt){
      Event.stop(evt);
      var elt = Event.element(evt);
      elt = (elt.nodeName!='A')?elt.up('a.'+this.widgetName):elt;
      elt.blur();

      this.overlayBox();
      this.loadingBox();
      $(this.idOverlay,this.idLoading).invoke('show');

      var url = this._getUrl(elt);
      var imgType = /\.(jpe?g|gif|png)/gi;
      var contentTitle = elt.title || elt.name || elt.caption || '';
      $(this.idTitle).update(this.closeHtml.evaluate({close: this.options.msgClose}) + contentTitle);
      Event.observe(this.idClose,'click',this.hide.bindAsEventListener(this),false);

      if (url.baseUrl.match(imgType)) {
         this.showImg(elt);
      }   else {
         this.show(elt);
      }
   },

   refresh: function(elt){
      this.show($(elt).up('a.gwidget-refresh'));
   },

   hide: function(evt){
      $(this.idOverlay,this.idFrame,this.idWindow,this.idLoading).invoke('hide');
      if (this.inlineRef){
         document.body.appendChild($(this.inlineRef).hide());
         this.inlineRef = false;
      }
      $(this.idContent).update('');
   },

   show: function(elt) {
      var urlInfo = this._getUrl(elt);
      var xWidth = (urlInfo.params.width || this.options.defaultWidth);
      var yHeight = (urlInfo.params.height || this.options.defaultHeight);
      var minBox = this.resizeBox({xWidth: xWidth, yHeight: yHeight});
      if (urlInfo.isXHR) {
         this._XHRUpdater(urlInfo.url,this.idContent);
      } else if ($(urlInfo.container)) {
         this.inlineRef = urlInfo.container;
         $(this.idContent).update('').appendChild($(urlInfo.container).show());
      } else {
         $(this.idContent).update(this.boxError.evaluate({msgError:this.errorInline}));
      }
      $(this.idContent).setStyle({overflow: 'auto',
         width: (Math.min(xWidth,minBox.xWidth)-this.options.contentPadding) + 'px',
         height: (Math.min(yHeight,minBox.yHeight)-2*this.options.contentPadding-5) + 'px'});
      this.windowBox(minBox).show();
      $(this.idLoading).hide();
   },

   showImg: function(elt) {
      var url = this._getUrl(elt);
      imgPreload = new Image();
    imgPreload.src = url.baseUrl;
      imgPreload.onload = function(){
         var imgDim = this.resizeBox({xWidth:imgPreload.width,yHeight:imgPreload.height});
         $(this.idContent).setStyle({width: imgDim.xWidth + 'px',height: imgDim.yHeight + 'px'});
         $(this.idContent).update(this.imgHtml.evaluate({url:url.baseUrl,width: imgDim.xWidth,height: imgDim.yHeight,alt:'title'}));
         $(this.idLoading).hide();
         this.windowBox({xWidth:imgDim.xWidth+this.options.contentPadding,yHeight:imgDim.yHeight+this.options.contentPadding*2}).show();
         imgPreload= null;
      }.bind(this);
   },

   overlayBox: function(){
      var page = this.pageDimensions();
      $(this.idOverlay).setStyle({ width: page.xWidth+'px', height: page.yHeight+'px' });
   },

   windowBox: function(dim){
      var view = this.viewportDimensions();
      var scroll = this.scrollPosition();
      $(this.idWindow).setStyle({
         width: (dim.xWidth) + 'px',
         height: (dim.yHeight) + 'px',
         left: ((view.xWidth - dim.xWidth)/2 + scroll.xOffset) + 'px' ,
         top: ((view.yHeight - dim.yHeight)/2 + scroll.yOffset) + 'px'
      });
      return $(this.idWindow);
   },

   loadingBox: function(){
      var view = this.viewportDimensions();
      var scroll = this.scrollPosition();
      $(this.idLoading).setStyle({
         left: ((view.xWidth - $(this.idLoading).getWidth())/2 + scroll.xOffset) + 'px',
         top: ((view.yHeight - $(this.idLoading).getHeight())/2 + scroll.yOffset) + 'px'
      });
   },

   resizeBox: function(dim){
      var viewport = this.viewportDimensions();
      var x = parseInt(viewport.xWidth - this.options.xMin); var y = parseInt(viewport.yHeight - this.options.yMin);
      var imgWidth = parseInt(dim.xWidth); var imgHeight = parseInt(dim.yHeight);
      if (imgWidth > x) {
         imgHeight = imgHeight * (x/imgWidth);
         imgWidth = x;
         if (imgHeight>y){
            imgWidth = imgWidth * (y/imgHeight);
            imgHeight = y;
         }
      } else if (imgHeight>y) {
            imgWidth = imgWidth*(y/imgHeight);
            imgHeight = y;
            if (imgWidth>x){
               imgHeight = imgHeight*(x/imgWidth);
               imgWidth = x;
            }
      }
      return {xWidth: parseInt(imgWidth),yHeight: parseInt(imgHeight)};
   }
});

/*
 * gTip
 * Gerald Estadieu
 * version 1.0 2007-02-20
 */
var gTip = gWidget.extend({
   widgetName: 'gtip',
   containerHtml: '<div class="gtip-container" style="display:none;"><div class="gtip-title"></div><div class="gtip-content"></div><span class="gtip-arrow">&nbsp;</span></div>',

   constructor: function(elt) {
      this.base();
      this.options.useRefresh = false;
      this.options.defaultWidth = 200;
      this.options.autoReload = false;
      if (elt) { this.observer($(elt)); }
      else { document.getElementsByClassName(this.widgetName).each(this.observer.bind(this));}
   },

   observer: function(elt) {
      var url = this._getUrl(elt);
      Event.observe(elt,(url.params.eventOn || this.options.eventOn),this.activate.bindAsEventListener(this),false);
      Event.observe(elt,(url.params.eventOff || this.options.eventOff),this.hide.bindAsEventListener(this),false);
      this.setup(elt);
   },

   setup: function(elt) {
      new Insertion.After($(elt),this.containerHtml);
      var gtip = $(elt).next('div.gtip-container');
      $(gtip).down('div.gtip-title').update( elt.title || elt.name || elt.caption || '');
      var url = this._getUrl(elt);
      if ($(url.container)) $(gtip).down('div.gtip-content').appendChild($(url.container).show());
   },

   activate: function(evt) {
      Event.stop(evt);
      this.hide();
      var elt = Event.element(evt);
      this.show(elt);
   },

   show: function(elt) {
      var gtip = $(elt).next('div.gtip-container');
      var url  = this._getUrl(elt);
      var arrow = $(gtip).down('span.gtip-arrow');
      arrow.removeClassName('gtip-arrow-left','gtip-arrow-right');

      var viewport = this.viewportDimensions();
      var eltpos   = Position.cumulativeOffset($(elt));
      var xWidth = (url.params.width || this.options.defaultWidth)*1;
      if (viewport.xWidth>(eltpos[0]+$(elt).getWidth()+xWidth)){
         var left = eltpos[0] + $(elt).getWidth() + 15;
         $(arrow).addClassName('gtip-arrow-left');
         $(arrow).setStyle({left:'-10px'});
      } else {
         var left = eltpos[0] - (xWidth*1+15);
         $(arrow).addClassName('gtip-arrow-right');
         $(arrow).setStyle({left:xWidth+'px'});
      }
      $(gtip).setStyle({
         width:xWidth+'px',
         height:((url.params.height)?url.params.height:'')+'px',
         top:(eltpos[1]-5)+'px',
         left:left+'px'}).show();
         /*if (this.options.toggleEffect) {
            Effect.toggle($(gtip),this.options.toggleEffect,{duration:0.3});
         }   else { $(gtip).show(); }
         */
      //setTimeout(this.hide.bind(this),10);
      if (url.isXHR && !$(elt).hasClassName(this.classXHRLoaded)) {
         this._XHRUpdater(url.url,$(gtip).down('div.gtip-content'));
         $(elt).addClassName(this.classXHRLoaded);
      }
   },

   hide: function(evt) {
      if (evt) {
         Event.stop(evt);
         var elt = Event.element(evt);
         //if (this.options.toggleEffect) { Effect.toggle($(elt).next('div.gtip-container'),this.options.toggleEffect,{duration:0.3});} else {
         if ($(elt).next('div.gtip-container')) $(elt).next('div.gtip-container').hide(); //}
      } else {
         document.getElementsByClassName('gtip-container').invoke('hide');
      }
   },

   cancel: function(evt) {
      Event.stop(evt);
   }
});


function gWidget_Init(){
   if (typeof(gWidget_Options) == 'undefined' || typeof(gWidget_Options.declarative) == 'undefined') {
      new gBox();
      new gExpander();
      document.getElementsByClassName('gtab').each(function(elt){
         new gTab(elt.readAttribute('id'));
      });
      new gTip();
   }
}

Event.observe(window,'load',gWidget_Init,false);
